/*
 * Copyright (c) 2010-2015 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include <Swift/QtUI/QtMainWindow.h>

#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/smart_ptr/make_shared.hpp>

#include <QAction>
#include <QBoxLayout>
#include <QComboBox>
#include <QLineEdit>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMenuBar>
#include <QPushButton>
#include <QTabWidget>
#include <QToolBar>

#include <Swiften/Base/Platform.h>

#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestHistoryUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h>
#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h>

#include <Swift/QtUI/QtAdHocCommandWithJIDWindow.h>
#include <Swift/QtUI/QtLoginWindow.h>
#include <Swift/QtUI/QtSettingsProvider.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/QtTabWidget.h>
#include <Swift/QtUI/QtUISettingConstants.h>
#include <Swift/QtUI/Roster/QtFilterWidget.h>
#include <Swift/QtUI/Roster/QtRosterWidget.h>
#if defined(SWIFTEN_PLATFORM_MACOSX)
#include <Swift/QtUI/CocoaUIHelpers.h>
#elif defined(SWIFTEN_PLATFORM_WINDOWS)
#include <Swift/QtUI/WinUIHelpers.h>
#else
#include <Swift/QtUI/QtCertificateViewerDialog.h>
#endif

namespace Swift {

QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist, bool enableAdHocCommandOnJID) : QWidget(), MainWindow(false), loginMenus_(loginMenus) {
	uiEventStream_ = uiEventStream;
	settings_ = settings;
	setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
	QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
	mainLayout->setContentsMargins(0,0,0,0);
	mainLayout->setSpacing(0);
	meView_ = new QtRosterHeader(settings, statusCache, this);
	mainLayout->addWidget(meView_);
	connect(meView_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleStatusChanged(StatusShow::Type, const QString&)));
	connect(meView_, SIGNAL(onEditProfileRequest()), this, SLOT(handleEditProfileRequest()));
	connect(meView_, SIGNAL(onShowCertificateInfo()), this, SLOT(handleShowCertificateInfo()));

	tabs_ = new QtTabWidget(this);
#if QT_VERSION >= 0x040500
	tabs_->setDocumentMode(true);
#endif
	tabs_->setTabPosition(QTabWidget::South);
	mainLayout->addWidget(tabs_);
	contactsTabWidget_ = new QWidget(this);
	contactsTabWidget_->setContentsMargins(0, 0, 0, 0);
	QBoxLayout *contactTabLayout = new QBoxLayout(QBoxLayout::TopToBottom, contactsTabWidget_);
	contactsTabWidget_->setLayout(contactTabLayout);
	contactTabLayout->setSpacing(0);
	contactTabLayout->setContentsMargins(0, 0, 0, 0);

	treeWidget_ = new QtRosterWidget(uiEventStream_, settings_, this);

	contactTabLayout->addWidget(treeWidget_);
	new QtFilterWidget(this, treeWidget_, uiEventStream_, contactTabLayout);

	tabs_->addTab(contactsTabWidget_, tr("&Contacts"));

	eventWindow_ = new QtEventWindow(uiEventStream_);
	connect(eventWindow_, SIGNAL(onNewEventCountUpdated(int)), this, SLOT(handleEventCountUpdated(int)));

	chatListWindow_ = new QtChatListWindow(uiEventStream_, settings_);
	connect(chatListWindow_, SIGNAL(onCountUpdated(int)), this, SLOT(handleChatCountUpdated(int)));

	tabs_->addTab(chatListWindow_, tr("C&hats"));
	tabs_->addTab(eventWindow_, tr("&Notices"));

	tabs_->setCurrentIndex(settings_->getSetting(QtUISettingConstants::CURRENT_ROSTER_TAB));

	connect(tabs_, SIGNAL(currentChanged(int)), this, SLOT(handleTabChanged(int)));

	tabBarCombo_ = NULL;
	if (settings_->getSetting(QtUISettingConstants::USE_SCREENREADER)) {
		tabs_->tabBar()->hide();
		tabBarCombo_ = new QComboBox(this);
		tabBarCombo_->setAccessibleName("Current View");
		tabBarCombo_->addItem(tr("Contacts"));
		tabBarCombo_->addItem(tr("Chats"));
		tabBarCombo_->addItem(tr("Notices"));
		tabBarCombo_->setCurrentIndex(tabs_->currentIndex());
		mainLayout->addWidget(tabBarCombo_);
		connect(tabBarCombo_, SIGNAL(currentIndexChanged(int)), tabs_, SLOT(setCurrentIndex(int)));
	}


	this->setLayout(mainLayout);

	QMenu* viewMenu = new QMenu(tr("&View"), this);
	menus_.push_back(viewMenu);

	compactRosterAction_ = new QAction(tr("&Compact Roster"), this);
	compactRosterAction_->setCheckable(true);
	compactRosterAction_->setChecked(false);
	connect(compactRosterAction_, SIGNAL(toggled(bool)), SLOT(handleCompactRosterToggled(bool)));
	viewMenu->addAction(compactRosterAction_);
	handleCompactRosterToggled(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER));

	showOfflineAction_ = new QAction(tr("&Show offline contacts"), this);
	showOfflineAction_->setCheckable(true);
	showOfflineAction_->setChecked(false);
	connect(showOfflineAction_, SIGNAL(toggled(bool)), SLOT(handleShowOfflineToggled(bool)));
	viewMenu->addAction(showOfflineAction_);
	handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE));

	if (emoticonsExist) {
		showEmoticonsAction_ = new QAction(tr("&Show Emoticons"), this);
		showEmoticonsAction_->setCheckable(true);
		showEmoticonsAction_->setChecked(false);
		connect(showEmoticonsAction_, SIGNAL(toggled(bool)), SLOT(handleShowEmoticonsToggled(bool)));
		viewMenu->addAction(showEmoticonsAction_);
		handleShowEmoticonsToggled(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS));
	}

	QMenu* actionsMenu = new QMenu(tr("&Actions"), this);
	menus_.push_back(actionsMenu);
	QAction* editProfileAction = new QAction(tr("Edit &Profile…"), this);
	connect(editProfileAction, SIGNAL(triggered()), SLOT(handleEditProfileAction()));
	actionsMenu->addAction(editProfileAction);
	onlineOnlyActions_ << editProfileAction;
	QAction* joinMUCAction = new QAction(tr("Enter &Room…"), this);
	connect(joinMUCAction, SIGNAL(triggered()), SLOT(handleJoinMUCAction()));
	actionsMenu->addAction(joinMUCAction);
	onlineOnlyActions_ << joinMUCAction;
#ifdef SWIFT_EXPERIMENTAL_HISTORY
	QAction* viewLogsAction = new QAction(tr("&View History…"), this);
	connect(viewLogsAction, SIGNAL(triggered()), SLOT(handleViewLogsAction()));
	actionsMenu->addAction(viewLogsAction);
#endif
	openBlockingListEditor_ = new QAction(tr("Edit &Blocking List…"), this);
	connect(openBlockingListEditor_, SIGNAL(triggered()), SLOT(handleEditBlockingList()));
	actionsMenu->addAction(openBlockingListEditor_);
	onlineOnlyActions_ << openBlockingListEditor_;
	openBlockingListEditor_->setVisible(false);
	addUserAction_ = new QAction(tr("&Add Contact…"), this);
	addUserAction_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D));
	addUserAction_->setShortcutContext(Qt::ApplicationShortcut);
	connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool)));
	actionsMenu->addAction(addUserAction_);
	onlineOnlyActions_ << addUserAction_;
	editUserAction_ = new QAction(tr("&Edit Selected Contact…"), this);
	connect(editUserAction_, SIGNAL(triggered(bool)), treeWidget_, SLOT(handleEditUserActionTriggered(bool)));
	actionsMenu->addAction(editUserAction_);
	onlineOnlyActions_ << editUserAction_;
	editUserAction_->setEnabled(false);
	chatUserAction_ = new QAction(tr("Start &Chat…"), this);
	chatUserAction_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
	chatUserAction_->setShortcutContext(Qt::ApplicationShortcut);
	connect(chatUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleChatUserActionTriggered(bool)));
	actionsMenu->addAction(chatUserAction_);
	onlineOnlyActions_ << chatUserAction_;
	if (enableAdHocCommandOnJID) {
		otherAdHocAction_ = new QAction(tr("Run Other Command"), this);
		connect(otherAdHocAction_, SIGNAL(triggered()), this, SLOT(handleOtherAdHocActionTriggered()));
		actionsMenu->addAction(otherAdHocAction_);
		onlineOnlyActions_ << otherAdHocAction_;
	}
	serverAdHocMenu_ = new QMenu(tr("Run Server Command"), this);
	actionsMenu->addMenu(serverAdHocMenu_);
	actionsMenu->addSeparator();
	QAction* signOutAction = new QAction(tr("&Sign Out"), this);
	connect(signOutAction, SIGNAL(triggered()), SLOT(handleSignOutAction()));
	actionsMenu->addAction(signOutAction);

	toggleRequestDeliveryReceipts_ = new QAction(tr("&Request Delivery Receipts"), this);
	toggleRequestDeliveryReceipts_->setCheckable(true);
	toggleRequestDeliveryReceipts_->setChecked(settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS));
	connect(toggleRequestDeliveryReceipts_, SIGNAL(toggled(bool)), SLOT(handleToggleRequestDeliveryReceipts(bool)));

	QList< QAction* > generalMenuActions = loginMenus_.generalMenu->actions();
	loginMenus_.generalMenu->insertAction(generalMenuActions.at(generalMenuActions.count()-2),toggleRequestDeliveryReceipts_);

	treeWidget_->onSomethingSelectedChanged.connect(boost::bind(&QtMainWindow::handleSomethingSelectedChanged, this, _1));

	setAvailableAdHocCommands(std::vector<DiscoItems::Item>());
	QAction* adHocAction = new QAction(tr("Collecting commands..."), this);
	adHocAction->setEnabled(false);
	serverAdHocMenu_->addAction(adHocAction);
	serverAdHocCommandActions_.append(adHocAction);

	settings_->onSettingChanged.connect(boost::bind(&QtMainWindow::handleSettingChanged, this, _1));
}

QtMainWindow::~QtMainWindow() {
	settings_->onSettingChanged.disconnect(boost::bind(&QtMainWindow::handleSettingChanged, this, _1));
}

void QtMainWindow::handleTabChanged(int index) {
	settings_->storeSetting(QtUISettingConstants::CURRENT_ROSTER_TAB, index);
}

void QtMainWindow::handleToggleRequestDeliveryReceipts(bool enabled) {
	settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, enabled);
}

void QtMainWindow::handleShowCertificateInfo() {
	onShowCertificateRequest();
}

void QtMainWindow::handleEditBlockingList() {
	uiEventStream_->send(boost::make_shared<RequestBlockListDialogUIEvent>());
}

void QtMainWindow::handleSomethingSelectedChanged(bool itemSelected) {
	bool isOnline = addUserAction_->isEnabled();
	editUserAction_->setEnabled(isOnline && itemSelected);
}

QtEventWindow* QtMainWindow::getEventWindow() {
	return eventWindow_;
}

QtChatListWindow* QtMainWindow::getChatListWindow() {
	return chatListWindow_;
}

void QtMainWindow::setRosterModel(Roster* roster) {
	treeWidget_->setRosterModel(roster);
}

void QtMainWindow::handleEditProfileRequest() {
	uiEventStream_->send(boost::make_shared<RequestProfileEditorUIEvent>());
}

void QtMainWindow::handleEventCountUpdated(int count) {
	QColor eventTabColor = (count == 0) ? QColor() : QColor(255, 0, 0); // invalid resets to default
	int eventIndex = 2;
	tabs_->tabBar()->setTabTextColor(eventIndex, eventTabColor);
	QString text = tr("&Notices");
	if (count > 0) {
		text += QString(" (%1)").arg(count);
	}
	tabs_->setTabText(eventIndex, text);
}

void QtMainWindow::handleChatCountUpdated(int count) {
	QColor chatTabColor = (count == 0) ? QColor() : QColor(255, 0, 0); // invalid resets to default
	int chatIndex = 1;
	tabs_->tabBar()->setTabTextColor(chatIndex, chatTabColor);
	QString text = tr("C&hats");
	if (count > 0) {
		text += QString(" (%1)").arg(count);
	}
	tabs_->setTabText(chatIndex, text);
}

void QtMainWindow::handleAddUserActionTriggered(bool /*checked*/) {
	boost::shared_ptr<UIEvent> event(new RequestAddUserDialogUIEvent());
	uiEventStream_->send(event);
}

void QtMainWindow::handleChatUserActionTriggered(bool /*checked*/) {
	boost::shared_ptr<UIEvent> event(new RequestChatWithUserDialogUIEvent());
	uiEventStream_->send(event);
}

void QtMainWindow::handleOtherAdHocActionTriggered() {
	new QtAdHocCommandWithJIDWindow(uiEventStream_);
}

void QtMainWindow::handleSignOutAction() {
	loginMenus_.generalMenu->removeAction(toggleRequestDeliveryReceipts_);
	onSignOutRequest();
}

void QtMainWindow::handleEditProfileAction() {
	uiEventStream_->send(boost::make_shared<RequestProfileEditorUIEvent>());
}

void QtMainWindow::handleJoinMUCAction() {
	uiEventStream_->send(boost::make_shared<RequestJoinMUCUIEvent>());
}

void QtMainWindow::handleViewLogsAction() {
	uiEventStream_->send(boost::make_shared<RequestHistoryUIEvent>());
}

void QtMainWindow::handleStatusChanged(StatusShow::Type showType, const QString &statusMessage) {
	onChangeStatusRequest(showType, Q2PSTRING(statusMessage));
}

void QtMainWindow::handleSettingChanged(const std::string& settingPath) {
	if (settingPath == SettingConstants::SHOW_OFFLINE.getKey()) {
		handleShowOfflineToggled(settings_->getSetting(SettingConstants::SHOW_OFFLINE));
	}
	if (settingPath == QtUISettingConstants::SHOW_EMOTICONS.getKey()) {
		handleShowEmoticonsToggled(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS));
	}
	if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) {
		toggleRequestDeliveryReceipts_->setChecked(settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS));
	}
	if (settingPath == QtUISettingConstants::COMPACT_ROSTER.getKey()) {
		handleCompactRosterToggled(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER));
	}
}

void QtMainWindow::handleCompactRosterToggled(bool state) {
	settings_->storeSetting(QtUISettingConstants::COMPACT_ROSTER, state);
	compactRosterAction_->setChecked(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER));
}

void QtMainWindow::handleShowOfflineToggled(bool state) {
	settings_->storeSetting(SettingConstants::SHOW_OFFLINE, state);
	showOfflineAction_->setChecked(settings_->getSetting(SettingConstants::SHOW_OFFLINE));
}

void QtMainWindow::handleShowEmoticonsToggled(bool state) {
	settings_->storeSetting(QtUISettingConstants::SHOW_EMOTICONS, state);
	showEmoticonsAction_->setChecked(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS));
}

void QtMainWindow::setMyNick(const std::string& nick) {
	meView_->setNick(P2QSTRING(nick));
}

void QtMainWindow::setMyJID(const JID& jid) {
	meView_->setJID(P2QSTRING(jid.toBare().toString()));
}

void QtMainWindow::setMyAvatarPath(const std::string& path) {
	meView_->setAvatar(P2QSTRING(path));
}

void QtMainWindow::setMyStatusText(const std::string& status) {
	meView_->setStatusText(P2QSTRING(status));
}

void QtMainWindow::setMyStatusType(StatusShow::Type type) {
	meView_->setStatusType(type);
	const bool online = (type != StatusShow::None);
	treeWidget_->setOnline(online);
	chatListWindow_->setOnline(online);
	foreach (QAction *action, onlineOnlyActions_) {
		action->setEnabled(online);
	}
	serverAdHocMenu_->setEnabled(online);
}

void QtMainWindow::setMyContactRosterItem(boost::shared_ptr<ContactRosterItem> contact) {
	meView_->setContactRosterItem(contact);
}

void QtMainWindow::setConnecting() {
	meView_->setConnecting();
}

void QtMainWindow::setStreamEncryptionStatus(bool tlsInPlaceAndValid) {
	meView_->setStreamEncryptionStatus(tlsInPlaceAndValid);
}

void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain) {
	openCertificateDialog(chain, this);
}

void QtMainWindow::openCertificateDialog(const std::vector<Certificate::ref>& chain, QWidget* parent) {
#if defined(SWIFTEN_PLATFORM_MACOSX)
	CocoaUIHelpers::displayCertificateChainAsSheet(parent, chain);
#elif defined(SWIFTEN_PLATFORM_WINDOWS)
	WinUIHelpers::displayCertificateChainAsSheet(parent, chain);
#else
	QtCertificateViewerDialog::displayCertificateChainAsSheet(parent, chain);
#endif
}

void QtMainWindow::handleAdHocActionTriggered(bool /*checked*/) {
	QAction* action = qobject_cast<QAction*>(sender());
	assert(action);
	DiscoItems::Item command = serverAdHocCommands_[serverAdHocCommandActions_.indexOf(action)];
	uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestAdHocUIEvent(command)));
}

void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands) {
	serverAdHocCommands_ = commands;
	foreach (QAction* action, serverAdHocCommandActions_) {
		delete action;
	}
	serverAdHocMenu_->clear();
	serverAdHocCommandActions_.clear();
	foreach (DiscoItems::Item command, commands) {
		QAction* action = new QAction(P2QSTRING(command.getName()), this);
		connect(action, SIGNAL(triggered(bool)), this, SLOT(handleAdHocActionTriggered(bool)));
		serverAdHocMenu_->addAction(action);
		serverAdHocCommandActions_.append(action);
	}
	if (serverAdHocCommandActions_.isEmpty()) {
		QAction* action = new QAction(tr("No Available Commands"), this);
		action->setEnabled(false);
		serverAdHocMenu_->addAction(action);
		serverAdHocCommandActions_.append(action);
	}
}

void QtMainWindow::setBlockingCommandAvailable(bool isAvailable) {
	openBlockingListEditor_->setVisible(isAvailable);
}

}

